
#include "xdisk.h"
#include "xfat.h"

static xfat_err_t parse_fat_header(xfat_t *xfat, dbr_t *dbr)
{
    xdisk_part_t *xdisk_part = xfat->disk_part;

    xfat->root_cluster = dbr->fat32.BPB_RootClus;
    xfat->fat_tbl_sectors = dbr->fat32.BPB_FATSz32;

    if (dbr->fat32.BPB_ExtFlags & (1 << 7))
    {
        u32_t table = dbr->fat32.BPB_ExtFlags & 0xF;
        xfat->fat_start_sector = dbr->bpb.BPB_RsvdSecCnt + xdisk_part->start_sector + table * xfat->fat_tbl_sectors;
        xfat->fat_tbl_nr = 1;
    }
    else
    {
        xfat->fat_start_sector = dbr->bpb.BPB_RsvdSecCnt + xdisk_part->start_sector;
        xfat->fat_tbl_nr = dbr->bpb.BPB_NumFATs;
    }

    xfat->sec_per_cluster = dbr->bpb.BPB_SecPerClus;
    xfat->total_sectors = dbr->bpb.BPB_TotSec32;
    xfat->cluster_byte_size = xfat->sec_per_cluster * dbr->bpb.BPB_BytsPerSec;

    return FS_ERR_EOK;
}

xfat_err_t xfat_prase(xfat_t *xfat, xdisk_part_t *xdisk_part)
{
    dbr_t *dbr = (dbr_t *)malloc(xdisk_part->disk->sector_size);
    xdisk_t *xdisk = xdisk_part->disk;
    xfat_err_t err;

    xfat->disk_part = xdisk_part;

    // 读取dbr参数区
    err = xdisk_read(xdisk, (u8_t *)dbr, xdisk_part->start_sector, 1);
    if (err < 0)
    {
        return err;
    }

    // 解析dbr参数中的fat相关信息
    err = parse_fat_header(xfat, dbr);
    if (err < 0)
    {
        free(dbr);
        return err;
    }

    free(dbr);

    // 先一次性全部读取FAT表: todo: 优化
    xfat->fat_buffer = (u8_t *)malloc(xfat->fat_tbl_sectors * xdisk->sector_size);
    err = xdisk_read(xdisk, (u8_t *)xfat->fat_buffer, xfat->fat_start_sector, xfat->fat_tbl_sectors);
    if (err < 0)
    {
        return err;
    }

    return FS_ERR_EOK;
}

u32_t cluster_first_sector(xfat_t *xfat, u32_t cluster_no)
{
    u32_t data_start_cluster = xfat->fat_start_sector + xfat->fat_tbl_sectors * xfat->fat_tbl_nr;
    return data_start_cluster + (cluster_no - 2) * xfat->sec_per_cluster;
}

xfat_err_t read_cluster(xfat_t *xfat, u8_t *buffer, u32_t cluster, u32_t count)
{
    xfat_err_t err = FS_ERR_EOK;
    u32_t i = 0;
    u8_t *cluster_buffer = buffer;
    u32_t curr_sector = cluster_first_sector(xfat, cluster);

    for (i = 0; i < count; i++)
    {
        err = xdisk_read(xfat->disk_part->disk, (u8_t *)cluster_buffer, curr_sector, xfat->sec_per_cluster);
        if (err != FS_ERR_EOK)
        {
            printf("read_cluster read sector <%d>, count <%d> err <%d>\r\n", curr_sector, xfat->fat_tbl_sectors, err);
            return err;
            /* code */
        }

        cluster_buffer += xfat->cluster_byte_size;
        curr_sector += xfat->sec_per_cluster;
    }
    return err;
}

u32_t is_cluster_valid(u32_t cluster)
{
    cluster &= 0x0fffffff;
    return ((cluster < 0x0ffffff0) && (cluster >= 0x02));
}

xfat_err_t get_next_cluster(xfat_t *xfat, u32_t curr_cluster_no, u32_t *next_cluster)
{
    xfat_err_t err = FS_ERR_EOK;
    if (is_cluster_valid(curr_cluster_no))
    {
        cluster32_t *cluster_buff = (cluster32_t *)xfat->fat_buffer;
        *next_cluster = cluster_buff[curr_cluster_no].s.next;
    }
    else
    {
        *next_cluster = CLUSTER_INVALID;
    }

    return err;
}

xfat_err_t dir_item_info_show(diritem_t *diritem)
{
    xfat_err_t err = FS_ERR_EOK;
    u8_t file_name[14] = {0};
    u8_t file_ext[4] = {0};
    memcpy(file_name, diritem->DIR_Name, 8);
    memcpy(file_ext, &diritem->DIR_Name[8], 3);

    if (file_name[0] == 0x05)
    {
        file_name[0] = 0xe5;
    }

    sprintf(file_name, "%s.%s", file_name, file_ext);

    printf("%s %d %d\r\n", file_name, diritem->DIR_FileSize, (diritem->DIR_FstClusHI << 16 | diritem->DIR_FstClusL0) );

    return err;
}
void buff_dump(u8_t *buff, u32_t size)
{
    for (int i = 0; i < size; i++)
    {
        if (i%32 == 0)
        {
            printf("\r\n");
        }
        printf(" %02x", buff[i]);
    }
}

xfat_err_t list_xfat_root_dir(xfat_t *xfat)
{
    xfat_err_t err = FS_ERR_EOK;
    u8_t *culster_buffer = malloc(xfat->cluster_byte_size);
    u32_t curr_cluster = xfat->root_cluster;
    diritem_t *dir_item;
    u32_t index=0;
    while (1)
    {
        if (is_cluster_valid(curr_cluster) == 0)
        {
            break;
        }

        err = read_cluster(xfat, culster_buffer, curr_cluster, 1);
        if (err != FS_ERR_EOK)
        {
            return err;
        }

        dir_item = (diritem_t *)culster_buffer;

        for (u32_t i = 0; i < xfat->cluster_byte_size / sizeof(diritem_t); i++)
        {
            u8_t dir_flag = dir_item->DIR_Name[0];
            if (dir_flag == DIRITEM_NAME_FREE)
            {
                continue;
            }

            if (dir_flag == DIRITEM_NAME_END)
            {
                break;
            }
            index++;
            printf("no %d ", index);
            dir_item_info_show(dir_item);
            dir_item ++;
        }

        err = get_next_cluster(xfat, curr_cluster, &curr_cluster);
        if (err)
        {
            printf("get next cluster failed， current cluster %d\n", curr_cluster);
            break;
        }
    }
    return err;
    free(culster_buffer);
}


xfat_err_t list_xfat_file_content(xfat_t *xfat, u32_t file_root_cluster)
{
    xfat_err_t err = FS_ERR_EOK;
    u8_t *culster_buffer = malloc(xfat->cluster_byte_size + 1);
    u32_t curr_cluster = file_root_cluster;
    u32_t index=0;
    while (1)
    {
        if (is_cluster_valid(curr_cluster) == 0)
        {
            break;
        }

        memset(culster_buffer, 0, xfat->cluster_byte_size + 1);

        err = read_cluster(xfat, culster_buffer, curr_cluster, 1);
        if (err != FS_ERR_EOK)
        {
            printf("read cluster failed， current cluster %d\n", curr_cluster);
            return err;
        }

        printf("%s", culster_buffer);

        err = get_next_cluster(xfat, curr_cluster, &curr_cluster);
        if (err)
        {
            printf("get next cluster failed， current cluster %d\n", curr_cluster);
            break;
        }
    }
    return err;
    free(culster_buffer);
}
